var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) =>
  key in obj
    ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value })
    : (obj[key] = value);
var __publicField = (obj, key, value) => {
  __defNormalProp(obj, typeof key !== 'symbol' ? key + '' : key, value);
  return value;
};
import { Vector2 } from 'three';
import * as _THREE from 'three';

class PLYExporter {
  constructor() {
    __publicField(this, 'uvs');
  }
  mergeVertices(geometry, tolerance = 1e-4) {
    this.uvs = [];
    // debugger
    tolerance = Math.max(tolerance, Number.EPSILON);
    const hashToIndex = {};
    const indices = geometry.getIndex();
    const positions = geometry.getAttribute('position');
    const uv = geometry.getAttribute('uv');
    const vertexCount = indices ? indices.count : positions.count;
    let nextIndex = 0;
    const attributeNames = Object.keys(geometry.attributes);

    const attrArrays = {};
    const morphAttrsArrays = {};
    const newIndices = [];
    const getters = ['getX', 'getY', 'getZ', 'getW'];
    for (let i = 0, l = attributeNames.length; i < l; i++) {
      const name = attributeNames[i];
      attrArrays[name] = [];
      const morphAttr = geometry.morphAttributes[name];
      if (morphAttr) {
        morphAttrsArrays[name] = new Array(morphAttr.length).fill(0).map(() => []);
      }
    }
    const decimalShift = Math.log10(1 / tolerance);
    const shiftMultiplier = Math.pow(10, decimalShift);
    for (let i = 0; i < vertexCount; i++) {
      const index = indices ? indices.getX(i) : i;
      let hash = '';
      // for (let j = 0, l = attributeNames.length; j < l; j++)
      {
        let j = 0;
        const name = attributeNames[j];
        const attribute = geometry.getAttribute(name);
        const itemSize = attribute.itemSize;
        for (let k = 0; k < itemSize; k++) {
          hash += `${~~(attribute[getters[k]](index) * shiftMultiplier)},`;
        }
      }

      if (geometry.hasAttribute('color')) {
        const color = geometry.getAttribute('color');
        hash += `${~~(color.getX(index) * 255)},${~~(color.getY(index) * 255)},${~~(
          color.getZ(index) * 255
        )},`;
      }

      if (hash in hashToIndex) {
        newIndices.push(hashToIndex[hash]);
        if (uv) {
          let tempVector2 = new Vector2(uv.getX(index), uv.getY(index));
          this.uvs.push(tempVector2);
        }
      } else {
        for (let j = 0, l = attributeNames.length; j < l; j++) {
          const name = attributeNames[j];
          const attribute = geometry.getAttribute(name);
          const morphAttr = geometry.morphAttributes[name];
          const itemSize = attribute.itemSize;
          const newarray = attrArrays[name];
          const newMorphArrays = morphAttrsArrays[name];
          for (let k = 0; k < itemSize; k++) {
            const getterFunc = getters[k];
            newarray.push(attribute[getterFunc](index));
            if (morphAttr) {
              for (let m = 0, ml = morphAttr.length; m < ml; m++) {
                newMorphArrays[m].push(morphAttr[m][getterFunc](index));
              }
            }
          }
        }
        hashToIndex[hash] = nextIndex;
        newIndices.push(nextIndex);
        if (uv) {
          let tempVector2 = new Vector2(uv.getX(index), uv.getY(index));
          this.uvs.push(tempVector2);
        }

        nextIndex++;
      }
    }
    const result = geometry.clone();
    for (let i = 0, l = attributeNames.length; i < l; i++) {
      const name = attributeNames[i];
      const oldAttribute = geometry.getAttribute(name);
      const buffer = new oldAttribute.array.constructor(attrArrays[name]);
      const attribute = new _THREE.BufferAttribute(
        buffer,
        oldAttribute.itemSize,
        oldAttribute.normalized,
      );
      result.setAttribute(name, attribute);
      if (name in morphAttrsArrays) {
        for (let j = 0; j < morphAttrsArrays[name].length; j++) {
          const oldMorphAttribute = geometry.morphAttributes[name][j];
          const buffer2 = new oldMorphAttribute.array.constructor(morphAttrsArrays[name][j]);
          const morphAttribute = new _THREE.BufferAttribute(
            buffer2,
            oldMorphAttribute.itemSize,
            oldMorphAttribute.normalized,
          );
          result.morphAttributes[name][j] = morphAttribute;
        }
      }
    }
    result.setIndex(newIndices);
    return result;
  }
  parse(geometry, options = {}) {
    // debugger
    if (!geometry.isBufferGeometry) {
      throw new Error('THREE.PLYExporter: Geometry must be a BufferGeometry.');
    }

    const defaultOptions = {
      binary: false,
      excludeAttributes: [],
      littleEndian: false,
    };
    options = Object.assign(defaultOptions, options);
    const excludeAttributes = options.excludeAttributes;

    const vertices = geometry.getAttribute('position');
    const normals = geometry.getAttribute('normal');
    const uvs = geometry.getAttribute('uv');
    const colors = geometry.getAttribute('color');
    const indices = geometry.getIndex();

    if (!vertices) {
      throw new Error('THREE.PLYExporter: Geometry must have position attribute.');
    }

    const vertexCount = vertices.count;
    const faceCount = indices ? indices.count / 3 : vertexCount / 3;

    let includeNormals = normals && !excludeAttributes.includes('normal');
    let includeUVs = uvs && !excludeAttributes.includes('uv');
    let includeColors = colors && !excludeAttributes.includes('color');
    let includeIndices = indices && !excludeAttributes.includes('index');

    let header = `ply\nformat ${
      options.binary
        ? options.littleEndian
          ? 'binary_little_endian'
          : 'binary_big_endian'
        : 'ascii'
    } 1.0\n`;
    if (options.textureName) {
      header += `comment TextureFile ${options.textureName}\n`;
    }

    header += `element vertex ${vertexCount}\nproperty float x\nproperty float y\nproperty float z\n`;
    // if (includeNormals) header += `property float nx\nproperty float ny\nproperty float nz\n`;
    // if (includeUVs) header += `property float s\nproperty float t\n`;
    if (includeColors) header += `property uchar red\nproperty uchar green\nproperty uchar blue\n`;
    if (includeIndices)
      header += `element face ${faceCount}\nproperty list uchar int vertex_indices\nproperty list uchar float texcoord\n`;
    header += 'end_header\n';

    let result = '';
    if (options.binary) {
      const encoder = new TextEncoder();
      const headerBin = encoder.encode(header);
      const vertexSize =
        4 * 3 + (includeNormals ? 4 * 3 : 0) + (includeUVs ? 4 * 2 : 0) + (includeColors ? 3 : 0);
      const faceSize = includeIndices ? faceCount * (4 * 3 + 1 + 4 * 6 + 1) : 0;
      const buffer = new ArrayBuffer(headerBin.length + vertexCount * vertexSize + faceSize);
      const view = new DataView(buffer);
      new Uint8Array(buffer).set(headerBin, 0);
      let offset = headerBin.length;
      for (let i = 0; i < vertexCount; i++) {
        view.setFloat32(offset, vertices.getX(i), options.littleEndian);
        offset += 4;
        view.setFloat32(offset, vertices.getY(i), options.littleEndian);
        offset += 4;
        view.setFloat32(offset, vertices.getZ(i), options.littleEndian);
        offset += 4;
        if (includeColors) {
          view.setUint8(offset++, Math.floor(colors.getX(i) * 255));
          view.setUint8(offset++, Math.floor(colors.getY(i) * 255));
          view.setUint8(offset++, Math.floor(colors.getZ(i) * 255));
        }
      }
      if (includeIndices) {
        for (let i = 0; i < indices.count; i += 3) {
          view.setUint8(offset++, 3);
          view.setUint32(offset, indices.getX(i), options.littleEndian);
          offset += 4;
          view.setUint32(offset, indices.getX(i + 1), options.littleEndian);
          offset += 4;
          view.setUint32(offset, indices.getX(i + 2), options.littleEndian);
          offset += 4;
          //if (includeUVs)
          view.setUint8(offset++, 6);
          if (this.uvs.length > 0) {
            {
              view.setFloat32(offset, this.uvs[i].x, options.littleEndian);
              offset += 4;
              view.setFloat32(offset, this.uvs[i].y, options.littleEndian);
              offset += 4;
            }
            {
              view.setFloat32(offset, this.uvs[i + 1].x, options.littleEndian);
              offset += 4;
              view.setFloat32(offset, this.uvs[i + 1].y, options.littleEndian);
              offset += 4;
            }
            {
              view.setFloat32(offset, this.uvs[i + 2].x, options.littleEndian);
              offset += 4;
              view.setFloat32(offset, this.uvs[i + 2].y, options.littleEndian);
              offset += 4;
            }
          }
        }
      }
      result = buffer;
    } else {
      let vertexList = '';
      let faceList = '';
      for (let i = 0; i < vertexCount; i++) {
        vertexList += `${vertices.getX(i).toFixed(6)} ${vertices.getY(i).toFixed(6)} ${vertices
          .getZ(i)
          .toFixed(6)}`;

        if (includeColors)
          vertexList += ` ${Math.floor(colors.getX(i) * 255)} ${Math.floor(
            colors.getY(i) * 255,
          )} ${Math.floor(colors.getZ(i) * 255)}`;
        vertexList += '\n';
      }
      if (includeIndices) {
        // for (let i = 0; i < indices.count; i += 3) {
        //   faceList += `3 ${indices.getX(i)} ${indices.getX(i + 1)} ${indices.getX(i + 2)} 6 `;
        // }

        for (let i = 0; i < indices.count; i += 3) {
          const idx1 = indices.getX(i);
          const idx2 = indices.getX(i + 1);
          const idx3 = indices.getX(i + 2);

          faceList += `3 ${idx1} ${idx2} ${idx3} 6`;
          //if (includeUVs)
          if (this.uvs.length > 0) {
            faceList += ` ${this.uvs[i].x.toFixed(6)} ${this.uvs[i].y.toFixed(6)}`;
            faceList += ` ${this.uvs[i + 1].x.toFixed(6)} ${this.uvs[i + 1].y.toFixed(6)}`;
            faceList += ` ${this.uvs[i + 2].x.toFixed(6)} ${this.uvs[i + 2].y.toFixed(6)}`;
          }
          faceList += '\n';
        }
      }
      result = header + vertexList + (includeIndices ? faceList : '');
    }
    return result;
  }
}
export { PLYExporter };
